﻿#region Imports

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
using bGallery.Core.Common;
using bGallery.Core.Data;
using bGallery.Core.Domain;
using bGallery.Core.Service;
using Spring.Data.NHibernate.Support;
using Spring.DataBinding;

#endregion

namespace bGallery.Web.User
{
	/// <summary>
	/// Gallery management page
	/// </summary>
	public partial class Gallery : Page
	{
		#region Private fields

		private Core.Domain.Album selectedAlbum;
		private Core.Domain.Photo selectedPhoto;
		private ICategoryDao categoryDao;
		private IAlbumDao albumDao;
		private IPhotoManager photoManager;
		private HttpPostedFile postedFile;
		private IPhototypeDao phototypeDao;
		private IPhotoDao photoDao;
		private ITextUtil textUtil;

		/// <summary>
		/// Server utility for html decoding
		/// </summary>
		private static HttpServerUtility Utility
		{
			get
			{
				if (HttpContext.Current != null)
					return HttpContext.Current.Server;
				else return null;
			}
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Gets currently selected album
		/// </summary>
		public Core.Domain.Album SelectedAlbum
		{
			get { return selectedAlbum; }
		}

		/// <summary>
		/// Gets currently selected photo
		/// </summary>
		public Core.Domain.Photo SelectedPhoto
		{
			get { return selectedPhoto; }
		}

		/// <summary>
		/// Sets category DAO
		/// </summary>
		public ICategoryDao CategoryDao
		{
			set { categoryDao = value; }
		}

		/// <summary>
		/// Sets album DAO
		/// </summary>
		public IAlbumDao AlbumDao
		{
			set { albumDao = value; }
		}

		/// <summary>
		/// Sets photo manager
		/// </summary>
		public IPhotoManager PhotoManager
		{
			set { photoManager = value; }
		}

		/// <summary>
		/// Sets posted file
		/// </summary>
		public HttpPostedFile PostedFile
		{
			set { postedFile = value; }
		}

		/// <summary>
		/// Sets phototype DAO
		/// </summary>
		public IPhototypeDao PhototypeDao
		{
			set { phototypeDao = value; }
		}

		/// <summary>
		/// Sets photo DAO
		/// </summary>
		public IPhotoDao PhotoDao
		{
			set { photoDao = value; }
		}

		/// <summary>
		/// Sets text util
		/// </summary>
		public ITextUtil TextUtil
		{
			set { textUtil = value; }
		}

		#endregion

		#region Event handlers
		/// <summary>
		/// Handles event on init
		/// </summary>
		/// <param name="e"></param>
		protected override void OnInit(EventArgs e)
		{
			if (SignedInUser.Id == 0)
				SetResult("Login");

			base.OnInit(e);
		}

		/// <summary>
		/// Handles on load event
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void Page_Load(object sender, EventArgs e)
		{
			if (!IsPostBack)
			{
				InitCategories();
				InitVisibility();
				InitAlbums();
				InitPhotos();
			}
		}

		/// <summary>
		/// Saves album
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void Save(object sender, EventArgs e)
		{
			CheckAlbumAbbreviation();

			using (new SessionScope())
			{
				albumDao.SaveOrUpdate(selectedAlbum);
			}
			SetResult("Album");
		}

		/// <summary>
		/// Cancels album editing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void Cancel(object sender, EventArgs e)
		{
			SetResult("Gallery");
		}

		/// <summary>
		/// Cancels photo editing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void CancelPhoto(object sender, EventArgs e)
		{
			SetResult("Album");
		}

		/// <summary>
		/// Sets album for editing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void EditAlbum(object sender, GridViewSelectEventArgs e)
		{
			selectedAlbum = albumDao.FindById((int)gvAlbums.DataKeys[e.NewSelectedIndex].Value, SignedInUser);

			// security break attempt
			if (selectedAlbum == null)
			{
				selectedAlbum = new Core.Domain.Album();
				selectedAlbum.Category = new Category();

				SetResult("Logout");
			}
			else
			{
				repPhotos.DataSource = selectedAlbum.Photos;
				repPhotos.DataBind();
			}
		}

		/// <summary>
		/// Deletes album
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void DeleteAlbum(object sender, GridViewDeleteEventArgs e)
		{
			using (new SessionScope())
			{
				Core.Domain.Album album = albumDao.FindById((int)gvAlbums.DataKeys[e.RowIndex].Value, SignedInUser);

				// delete photos
				foreach (Core.Domain.Photo photo in album.Photos)
				{
					DeletePhoto(photo);
				}

				// delete album
				albumDao.Delete(album);
			}
			SetResult("Gallery");
		}

		/// <summary>
		/// Save photo
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void SavePhoto(object sender, EventArgs e)
		{
			CheckPhotoAbbreviation();

			if (postedFile != null && postedFile.ContentLength > 0)
			{
				Core.Domain.Photo tempPhoto =
					photoManager.Upload(postedFile, Server.MapPath(Constants.basePath + "/user" + SignedInUser.Id),
					                    Server.MapPath(Constants.thumbPath + "/user" + SignedInUser.Id),
					                    Server.MapPath(Constants.detailPath + "/user" + SignedInUser.Id));
				selectedPhoto.Phototype = phototypeDao.FindByMimeOrCreate(tempPhoto.Phototype);
				selectedPhoto.Size = tempPhoto.Size;
				selectedPhoto.Filename = tempPhoto.Filename;
				selectedPhoto.Width = tempPhoto.Width;
				selectedPhoto.Height = tempPhoto.Height;

				using (new SessionScope())
				{
					photoDao.SaveOrUpdate(selectedPhoto);
				}
				SetResult("Album");
			}
			else if(selectedPhoto.Id > 0)
			{
				using (new SessionScope())
				{
					photoDao.SaveOrUpdate(selectedPhoto);
				}
				SetResult("Album");
			}
		}

		/// <summary>
		/// Loads photo for editing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void EditPhoto(object sender, EventArgs e)
		{
			int id = int.Parse(((LinkButton) sender).CommandArgument);
			selectedPhoto = photoDao.FindById(id);
		}

		/// <summary>
		/// Deletes photo
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void DeletePhoto(object sender, EventArgs e)
		{
			int id = int.Parse(((LinkButton)sender).CommandArgument);
			using (new SessionScope())
			{
				Core.Domain.Photo photo = photoDao.FindById(id);
				DeletePhoto(photo);
				SetResult("Album");
			}
		}

		#endregion

		#region Model Management and Data Binding Methods

		/// <summary>
		/// Initializes data model
		/// </summary>
		protected override void InitializeModel()
		{
			if(selectedAlbum == null)
			{
				selectedAlbum = new Core.Domain.Album();
				selectedAlbum.Category = new Category();
			}

			selectedPhoto = new Core.Domain.Photo();
			selectedPhoto.Phototype = new Phototype();
		}

		/// <summary>
		/// Loads model to page
		/// </summary>
		/// <param name="savedModel"></param>
		protected override void LoadModel(object savedModel)
		{
			selectedAlbum = (Core.Domain.Album)((Hashtable)savedModel)["album"];
			selectedPhoto = (Core.Domain.Photo)((Hashtable)savedModel)["photo"];
		}

		/// <summary>
		/// Saves data model from page
		/// </summary>
		/// <returns></returns>
		protected override object SaveModel()
		{
			Hashtable model = new Hashtable();
			model["album"] = selectedAlbum;
			model["photo"] = selectedPhoto;

			return model;
		}

		/// <summary>
		/// Initializes data bindings
		/// </summary>
		protected override void InitializeDataBindings()
		{
			// Album bindings
			BindingManager.AddBinding("tbName.Text", "SelectedAlbum.Name");
			BindingManager.AddBinding("tbAbbreviation.Text", "SelectedAlbum.Abbreviation", BindingDirection.TargetToSource);
			BindingManager.AddBinding("tbDescription.Text", "SelectedAlbum.Description");

			BindingManager.AddBinding("ParseVisibilityValue(ddlVisibility.SelectedValue)", "SelectedAlbum.Visibility",
									BindingDirection.SourceToTarget);
			BindingManager.AddBinding("ddlVisibility.SelectedValue", "SelectedAlbum.Visibility",
									BindingDirection.TargetToSource);

			BindingManager.AddBinding("@(CategoryDao).FindById(ddlCategory.SelectedValue)", "SelectedAlbum.Category",
									BindingDirection.SourceToTarget);
			BindingManager.AddBinding("ddlCategory.SelectedValue", "SelectedAlbum.Category.Id.ToString()",
									BindingDirection.TargetToSource);

			BindingManager.AddBinding("DateTime.Now", "SelectedAlbum.Created", BindingDirection.SourceToTarget);
			BindingManager.AddBinding("SignedInUser", "SelectedAlbum.Owner", BindingDirection.SourceToTarget);

			// Photo bindings
			BindingManager.AddBinding("tbPhotoName.Text", "SelectedPhoto.Name");
			BindingManager.AddBinding("tbPhotoAbbreviation.Text", "SelectedPhoto.Abbreviation", BindingDirection.TargetToSource);
			BindingManager.AddBinding("tbPhotoDescription.Text", "SelectedPhoto.Description");
			BindingManager.AddBinding("SelectedAlbum", "SelectedPhoto.Album", BindingDirection.SourceToTarget);
			BindingManager.AddBinding("DateTime.Now", "SelectedPhoto.Inserted", BindingDirection.SourceToTarget);
			BindingManager.AddBinding("pnlPhotos.Visible", "SelectedAlbum.ID != 0", BindingDirection.TargetToSource);
			BindingManager.AddBinding("fuPhoto.Enabled", "SelectedPhoto.ID == 0", BindingDirection.TargetToSource);
			BindingManager.AddBinding("fuPhoto.PostedFile", "PostedFile", BindingDirection.SourceToTarget);
		}

		#endregion

		#region Private methods

		/// <summary>
		/// Deletes photo
		/// </summary>
		/// <param name="photo"></param>
		private void DeletePhoto(Core.Domain.Photo photo)
		{
			// delete images
			string imagePath = Path.Combine(Path.Combine(Server.MapPath(Constants.basePath), "user" + SignedInUser.Id), photo.Filename);
			if (File.Exists(imagePath))
				File.Delete(imagePath);
			string thumbnailPath = Path.Combine(Path.Combine(Server.MapPath(Constants.thumbPath), "user" + SignedInUser.Id), photo.Filename);
			if (File.Exists(thumbnailPath))
				File.Delete(thumbnailPath);
			string detailPath = Path.Combine(Path.Combine(Server.MapPath(Constants.detailPath), "user" + SignedInUser.Id), photo.Filename);
			if (File.Exists(detailPath))
				File.Delete(detailPath);

			photoDao.Delete(photo);
		}

		/// <summary>
		/// Initializes visibility dropdownlist
		/// </summary>
		private void InitVisibility()
		{
			ListItemCollection items = new ListItemCollection();
			foreach (string name in Enum.GetNames(typeof(Visibility)))
			{
				ListItem item = new ListItem(GetMessage("visibility." + name), name);
				items.Add(item);
			}
			ddlVisibility.DataSource = items;
			ddlVisibility.DataTextField = "Text";
			ddlVisibility.DataValueField = "Value";
			ddlVisibility.DataBind();
		}

		/// <summary>
		/// Initializes category dropdownlist
		/// </summary>
		private void InitCategories()
		{
			ListItemCollection items = new ListItemCollection();
			FillFlatCategoryListItems(categoryDao.GetTree(null), items, 0);

			ddlCategory.DataSource = items;
			ddlCategory.DataTextField = "Text";
			ddlCategory.DataValueField = "Value";
			ddlCategory.DataBind();
		}

		/// <summary>
		/// Initializes albums grid
		/// </summary>
		private void InitAlbums()
		{
			gvAlbums.DataSource = albumDao.FindAllForOwner(SignedInUser);
			gvAlbums.DataBind();
		}

		/// <summary>
		/// Initializes photos
		/// </summary>
		private void InitPhotos()
		{
			// check selected album
			if (Request.Params != null && Request.Params["album"] != null)
			{
				selectedAlbum = albumDao.FindByAbbreviation(Request.Params["album"], SignedInUser);
				if (selectedAlbum != null)
				{
					repPhotos.DataSource = selectedAlbum.Photos;
					repPhotos.DataBind();
				}
			}
		}

		/// <summary>
		/// Return flat category items list
		/// </summary>
		/// <returns></returns>
		private static void FillFlatCategoryListItems(IEnumerable<Category> cats, ListItemCollection items, int level)
		{
			foreach (Category cat in cats)
			{
				// edit name according to level
				StringBuilder name = new StringBuilder();
				for (int i = 0; i < level; i++)
				{
					name.Append("&#160;&#160;");
				}

				if (level > 0)
					name.Append("|&#160;");
				
				name.Append(cat.Name);

				// add item to collection
				ListItem item = new ListItem(Decode(name.ToString()), cat.Id.ToString());
				items.Add(item);

				// add all sub categories
				if(cat.SubCategories.Count > 0)
				{
					FillFlatCategoryListItems(cat.SubCategories, items, (level + 1));
				}
			}
		}

		/// <summary>
		/// Checks if the abbreviation is unique and in
		/// other case makes it unique
		/// </summary>
		private void CheckAlbumAbbreviation()
		{
			selectedAlbum.Abbreviation = textUtil.GetUrlFriendlyName(selectedAlbum.Name);
			string newAbbreviation = selectedAlbum.Abbreviation;
			int level = 1;

			while (!albumDao.IsUnique(newAbbreviation, selectedAlbum.Id))
			{
				newAbbreviation = selectedAlbum.Abbreviation + level++;
			}

			selectedAlbum.Abbreviation = newAbbreviation;
		}

		/// <summary>
		/// Checks if the abbreviation is unique and in
		/// other case makes it unique
		/// </summary>
		private void CheckPhotoAbbreviation()
		{
			selectedPhoto.Abbreviation = textUtil.GetUrlFriendlyName(selectedPhoto.Name);
			string newAbbreviation = selectedPhoto.Abbreviation;
			int level = 1;

			while (!photoDao.IsUnique(newAbbreviation, selectedPhoto.Id))
			{
				newAbbreviation = selectedPhoto.Abbreviation + level++;
			}

			selectedPhoto.Abbreviation = newAbbreviation;
		}
		#endregion

		#region Public methods
		
		/// <summary>
		/// Parses string value of visibility enum
		/// </summary>
		/// <param name="value">String value to be parsed</param>
		/// <returns>Visibility enum value</returns>
		public Visibility ParseVisibilityValue(string value)
		{
			return (Visibility) Enum.Parse(typeof (Visibility), value);
		}

		/// <summary>
		/// Decodes html string
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		public static string Decode(string text)
		{
			return Utility.HtmlDecode(text);
		}

		#endregion
	}
}
